// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <memory>
#include <string>

#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/upbit/api_util/auth_key.h"

namespace coin2::exchange::order {
class UpbitRestParams;
class OrderConnectionConfig;
}  // namespace coin2::exchange::order

namespace coin2::exchange::base::order {
struct OrderExecution;
struct OrderSpec;
}  // namespace coin2::exchange::base::order

class UpbitNativePrivateClient : public coin2::exchange::base::order::NativePrivateClient {
 public:
  using Base = coin2::exchange::base::order::NativePrivateClient;
  using RestParams = coin2::exchange::order::UpbitRestParams;
  using NativeClientConfig = coin2::exchange::order::OrderConnectionConfig;
  using OrderExecution = coin2::exchange::base::order::OrderExecution;
  using OrderSpec = coin2::exchange::base::order::OrderSpec;
  using IOrderContext = coin2::exchange::base::order::IOrderContext;
  using HttpContextPtr = std::shared_ptr<AsioHttpContext>;
  using ActionType = coin2::exchange::base::order::NativePrivateClient::ActionType;

 public:
  std::shared_ptr<AsioHttpContext> SubmitOrderExecution(const OrderExecutionProto& order);
  std::shared_ptr<AsioHttpContext> SubmitRestQuery(const RestQueryProto& query);

 public:
  explicit UpbitNativePrivateClient(const MarketExchangeApi& mea, const NativeClientConfig& config);

  HttpContextPtr QueryAccountBalance();
  HttpContextPtr QueryOrders(const RestParams& params);
  HttpContextPtr QueryTrades(const RestParams& params);
  HttpContextPtr PlaceOrder(const RestParams& params);
  HttpContextPtr CancelOrder(const RestParams& params);
  HttpContextPtr CancelAllOrder(const RestParams& params);

  HttpContextPtr PlaceOrderNorm(const OrderExecution&) override;
  HttpContextPtr CancelOrderNorm(const OrderExecution&) override;

  RestParams GetRestOrderParams(const OrderSpec& order_spec, int64_t proc_order_id);
  RestParams GetRestCancelParams(const IOrderContext& order_context);

  UpbitRestAuth* GetAuth() const override { return dynamic_cast<UpbitRestAuth*>(Base::GetAuth()); }

  template <typename T, typename P, typename D>
  std::string ToList(T&& obj_list, P&& prefix, D&& delim) {
    bool first = true;
    std::stringstream ss;
    for (const auto& obj : obj_list) {
      if (first) {
        first = false;
        ss << prefix << "=" << obj;
      } else {
        ss << delim << prefix << "=" << obj;
      }
    }
    return ss.str();
  }

 private:
  const std::string path_query_account_balance_ = "/v1/accounts";
  const std::string path_query_trade_ = "/v1/order";
  const std::string path_query_trades_ = "/v1/orders";
  const std::string path_query_orders_ = "/v1/orders";
  const std::string path_place_order_ = "/v1/orders";
  const std::string path_cancel_order_ = "/v1/order";
  const std::string path_cancel_orders_ = "";

  // static constants
  static const MarketExchangeApi UPBIT_KR;
  static const MarketExchangeApi UPBIT_ID;
  static const MarketExchangeApi UPBIT_SG;
  static const MarketExchangeApi UPBIT_TH;
};
